package com.louis.springboot.demo.security;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.louis.springboot.demo.model.User;
import com.louis.springboot.demo.service.UserService;

/**
 * 用户登录认证信息查询
 * @author Louis
 * @date Jun 29, 2019
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserService userService;

    /*
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
    该方法很容易理解：通过用户名来加载用户 。这个方法主要用于从系统数据中
    查询并加载具体的用户到Spring Security中。
    从上面UserDetailsService 可以知道最终交给Spring Security的是UserDetails 。该接口是提供用户信息的核心接口。该接口实现仅仅存储用户的信息。后续会将该接口提供的用户信息封装到认证对象Authentication中去。UserDetails 默认提供了：
    用户的权限集， 默认需要添加ROLE_ 前缀
    用户的加密后的密码， 不加密会使用{noop}前缀
    应用内唯一的用户名
    账户是否过期
    账户是否锁定
    凭证是否过期
     用户是否可用

     */
    //UserDetails => Spring Security基础接口，包含某个用户的账号，
    //密码，权限，状态（是否锁定）等信息。只有getter方
    //构建UserDetails

    /**
     *
     可将用户信息存入缓存，减少数据库访问
     */
    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        User user = userService.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("该用户不存在");
        }
        // 用户权限列表，根据用户拥有的权限标识与如 @PreAuthorize("hasAuthority('sys:menu:view')") 标注的接口对比，决定是否可以调用接口
        Set<String> permissions = userService.findPermissions(username);
        List<GrantedAuthority> grantedAuthorities =
                permissions.stream().map(GrantedAuthorityImpl::new).collect(Collectors.toList());
        return new JwtUserDetails(username,
                user.getPassword(), grantedAuthorities);
    }
}